import { defineEventHandler, getQuery, getRouterParam } from "h3"; import { R } from "#server/utils/response"; import { resolveCharacterIdentity } from "#server/service/character/identity"; import { getCharacterSessionByIdAndUser } from "#server/service/character/session"; import { getCharacterCardBySlug } from "#server/service/character/character-card"; import { getCharacterStreamBuffer, subscribeToCharacterBuffer } from "#server/service/character/stream-buffer"; import log4js from "logger"; const logger = log4js.getLogger("APP"); export default defineEventHandler(async (event) => { const slug = getRouterParam(event, "slug"); if (!slug) { throw createError({ statusCode: 400, statusMessage: "参数无效" }); } const card = await getCharacterCardBySlug(slug); if (!card) { throw createError({ statusCode: 404, statusMessage: "角色不存在" }); } const identity = await resolveCharacterIdentity(event); if (!identity.userId && !identity.tempToken) { throw createError({ statusCode: 401, statusMessage: "请先登录或创建会话" }); } const query = getQuery(event); const sessionId = query.sessionId as string | undefined; if (!sessionId) { throw createError({ statusCode: 400, statusMessage: "缺少 sessionId 参数" }); } const session = await getCharacterSessionByIdAndUser(sessionId, identity.userId, identity.tempToken); if (!session) { throw createError({ statusCode: 404, statusMessage: "会话不存在" }); } if (session.cardId !== card.id) { throw createError({ statusCode: 400, statusMessage: "会话不属于该角色" }); } const buf = getCharacterStreamBuffer(sessionId); if (!buf) { return R.success({ active: false, reason: "no-buffer" }); } if (buf.done && buf.chunks.length === 0) { return R.success({ active: false, reason: "already-done", modelId: buf.modelId }); } const stream = new ReadableStream({ start(controller) { for (const chunk of buf.chunks) { controller.enqueue(chunk); } if (buf.done) { controller.close(); return; } const unsubscribe = subscribeToCharacterBuffer( sessionId, (chunk) => { try { controller.enqueue(chunk); } catch (e) { logger.error("[CHAR-STREAM-RESUME] enqueue error: %s", e instanceof Error ? e.message : String(e)); } }, () => { try { controller.close(); } catch { } }, ); event.node.req.on("close", () => { unsubscribe(); try { controller.close(); } catch { } }); }, }); return new Response(stream, { headers: { "Content-Type": "text/event-stream; charset=utf-8", "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Stream-Resume": "true", "X-Stream-Model-Id": String(buf.modelId ?? ""), "X-Stream-User-Message-Id": buf.userMessageId ?? "", }, }); });